package com.rad.net.protocol.android.encoder;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import java.util.TreeMap;

import com.rad.net.protocol.android.encoder.ExtraParam.RequestMethod;
import com.rad.net.protocol.android.utils.LogUtils;
import com.rad.net.protocol.common.annotation.Alias;
import com.rad.net.protocol.common.annotation.POST;
import com.rad.net.protocol.common.cmd.BaseCmd;


/** 
 * 类说明： 默认的编码器，该类根据cmd获取"M"命令，设置额外参数，根据参数和密钥生成校验码
 * @author  xinhui.cheng
 * @date    2014-8-20
 * @version 1.0
 */
public class DefaultEncoder extends BaseEncoder {

	private static final String CMD_SUFFIX = "Cmd";
	private static final String CHECKSIGN = "checkSign";
	private static final String M = "m";
	
	private String mSecretKey = "";
	private ArrayList<ExtraParam> mExtraParams = new ArrayList<ExtraParam>();

	/**
	 * 构造一个带密钥的对象
	 * @param secretKey 自定义密钥
	 */
	public DefaultEncoder(String secretKey){
		mSecretKey = secretKey;
		//因为checkSign是自动生成的，不是手动设置的，所以需要过滤
		addFilterFieldName(CHECKSIGN);
	}
	
	/**
	 * 根据键值对参数生成校验码
	 * @param params 有序的键值对参数
	 * @return 经过排序和md5加密后的字符串
	 */
	protected String createCheckSign(TreeMap<String, Object> params) {
		if(params == null || params.size() == 0){
			return null;
		}
		StringBuilder result = new StringBuilder();
		for(Entry<String, Object> entry : params.entrySet()){
			Object value = entry.getValue();
			if(value != null){
				if(value instanceof List){
					List<?> list = (List<?>) value;
					if(list.size() > 0){
						//将List中的对象全部转为String后进行排序
						ArrayList<String> strList = new ArrayList<String>();
						for(Object o : list){
							strList.add(String.valueOf(o));
						}
						Collections.sort(strList);
						for(String s : strList){
							result.append(s + "&");
						}
					} else {
						result.append("&");
					}
				} else {
					result.append(value.toString()+"&");
				}
			} else {
				result.append("&");
			}
		}
		result.append(mSecretKey);
		LogUtils.i(DefaultEncoder.class.getSimpleName(), "checkSignStr:"+result.toString());
		return md5(result.toString());
	}

	@Override
	protected List<ExtraParam> getExtraParams(TreeMap<String, Object> basicParams) {
		if(basicParams == null){
			basicParams = new TreeMap<String, Object>();
		}
		BaseCmd cmd = getBaseCmd();
		 //去掉M命令
		Alias alias = cmd.getClass().getAnnotation(Alias.class);
	   
		String m = null;
		if(alias == null){
			m = cmd.getClass().getSimpleName();
			//验证继承于BaseCmd的子类是否以”Cmd“结尾
			if(!m.endsWith(CMD_SUFFIX)){
				throw new IllegalArgumentException("CMD object must end with 'Cmd' ");
			} else {
				m = m.substring(0, m.lastIndexOf(CMD_SUFFIX));
			}
		} else {
			m = alias.value();
		}
		ArrayList<ExtraParam> params = new ArrayList<ExtraParam>();
		params.addAll(mExtraParams);
		RequestMethod method = getClassRequestMethod(cmd.getClass());
		//m参数放到GET参数中，方便服务器区分
		params.add(new ExtraParam(M, m,RequestMethod.GET , String.class)); // 去掉M命令
		for (ExtraParam param : params) {
			basicParams.put(param.getKey(), param.getValue());
		}
		try {
			//获取BaseCmd的checkSign字段
			Field field = BaseCmd.class.getDeclaredField(CHECKSIGN);
			String key = getFieldAlias(field);
			String value = createCheckSign(basicParams);
			params.add(new ExtraParam(key, value, method , String.class));
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		}
		
		return params;
	}
	
	/**
	 * 获取字段对象的别名
	 * @param field 字段对象
	 * @return 别名 或 字段名称 或 null
	 */
	public String getFieldAlias(Field field){
		if(field == null){
			return null;
		} 
		Alias alias = field.getAnnotation(Alias.class);
		if(alias != null){
			return alias.value();
		} else {
			return field.getName();
		}
	}
	
	/**
	 * 获取请求的方法
	 * @param clazz cmd的class对象
	 * @return {@link RequestMethod#POST}或者{@link RequestMethod#GET}
	 */
	public RequestMethod getClassRequestMethod(Class<? extends BaseCmd> clazz){
		POST post = clazz.getAnnotation(POST.class);
		if(post != null){
			return RequestMethod.POST;
		} else {
			return RequestMethod.GET;
		}
	}
	/**
	 * 添加自定义参数，该参数会参与到生成校验码的计算
	 * @param key 参数的键
	 * @param value 参数的值
	 * @param method 参数提交的方法
	 * @param valueClass value的类型
	 */
	public void addExtraParam(String key ,String value,RequestMethod method, Class<?> valueClass){
		addExtraParam(new ExtraParam(key, value, method, valueClass));
	}
	/**
	 * 添加自定义参数，该参数会参与到生成校验码的计算
	 * @param param
	 */
	public void addExtraParam(ExtraParam param){
		mExtraParams.add(param);
	}

}
